import {
  useCallback,
  useEffect,
  useMemo,
  useRef,
  useState,
} from "react";
import {
  ReactFlow,
  MiniMap,
  Background,
  useNodesState,
  useEdgesState,
  addEdge,
  BackgroundVariant,
  ReactFlowProvider,
  useReactFlow,
  Controls,
  Panel,
  ControlButton
} from "@xyflow/react";

import "@xyflow/react/dist/style.css";
import CustomEdge from "../flow/CustomEdge";
import LlmNode from "../flow/nodes/LlmNode";
import "dayjs/locale/zh-cn";
import { Button, FloatButton, message, Space } from "antd";
import KnowledgeNode from "../flow/nodes/KnowledgeNode";
import OutputNode from "../flow/nodes/OutputNode";
import AiChat from "./chat/AiChat";
import InputNode from "../flow/nodes/InputNode";
import { DnDProvider, useDnD } from "../context/DnDContext";
import { getLlmView, saveLlmView } from "../api/request";
import _ from "lodash";
import IfNode from "../flow/nodes/IfNode";
import HttpNode from "../flow/nodes/HttpNode";
import CodeNode from "../flow/nodes/CodeNode";
import Api from "../components/Api";
import Log from "../components/Log";
import GraphRag from "../components/GraphRag";
import ConnectionLine from "../flow/ConnectionLine";
import TooltipButton from "../components/TooltipButton";
import { useNavigate } from "react-router-dom";
import ELK from "elkjs";
import IterationNode from "../flow/nodes/IterationNode";
import LlmTabs from "../components/LlmTabs";
import useInitVariable from "@/hooks/useInitVariable";
import WorkflowNode from "@/flow/nodes/WorkflowNode";

interface NodeTypes {
  [key: string]: React.ComponentType<Node>;
}

/**
 * 节点整理
 */
const elk = new ELK();

const elkOptions = {
  'elk.algorithm': 'layered',
  'elk.layered.spacing.nodeNodeBetweenLayers': '480',
  'elk.spacing.nodeNode': '280',
};

const getLayoutedElements = (nodes, edges, options = {}) => {
  const isHorizontal = options?.['elk.direction'] === 'RIGHT';
  const graph = {
    id: 'root',
    layoutOptions: options,
    children: nodes.map((node) => ({
      ...node,
      targetPosition: isHorizontal ? 'left' : 'top',
      sourcePosition: isHorizontal ? 'right' : 'bottom',
      width: 0,
      height: 0,
    })),
    edges: edges,
  };

  return elk
    .layout(graph)
    .then((layoutedGraph) => ({
      nodes: layoutedGraph.children.map((node) => ({
        ...node,
        position: { x: node.x, y: node.y },
      })),

      edges: layoutedGraph.edges,
    }))
    .catch(console.error);
};

const DnDFlow = () => {
  const [nodes, setNodes, onNodesChange] = useNodesState([]);
  const [edges, setEdges, onEdgesChange] = useEdgesState([]);
  const { screenToFlowPosition, fitView } = useReactFlow();
  const [type] = useDnD();
  const nodeTypes = useMemo(
    () => ({
      llmNode: LlmNode,
      knowledgeNode: KnowledgeNode,
      outputNode: OutputNode,
      inputNode: InputNode,
      ifNode: IfNode,
      httpNode: HttpNode,
      codeNode: CodeNode,
      iterationNode: IterationNode,
      workflowNode: WorkflowNode
    }),
    []
  );
  const [chatVisible, setChatVisible] = useState(false);
  const [api, setApi] = useState(false)
  const [log, setLog] = useState(false)
  const [graph, setGraph] = useState(false)
  const navigate = useNavigate();
  const [projectName, setProjectName] = useState("")
  const { deteleVariable } = useInitVariable();

  /** 重排序 */
  const onLayout = useCallback(
    ({ direction }) => {
      const opts = { 'elk.direction': direction, ...elkOptions };
      const ns = nodes;
      const es = edges;

      getLayoutedElements(ns, es, opts).then(
        ({ nodes: layoutedNodes, edges: layoutedEdges }: any) => {
          setNodes((newNodes) => {
            newNodes.forEach((node, index) => {
              node.position = layoutedNodes[index].position
            })
            return _.cloneDeep(newNodes)
          })
          window.requestAnimationFrame(() => fitView());
        },
      );
    },
    [nodes, edges],
  );

  const onConnect = useCallback(
    (connection) => {

      const edge = { ...connection, type: "custom-edge" };
      setEdges((eds) => addEdge(edge, eds));
    },
    [setEdges]
  );

  const selectNodeId = useRef<string>("");

  // 自定义连线
  const edgeTypes = {
    "custom-edge": CustomEdge,
  };

  const onDragOver = useCallback((event) => {
    event.preventDefault();
    event.dataTransfer.dropEffect = "move";
  }, []);

  const onDrop = useCallback(
    (event) => {
      event.preventDefault();

      if (!type) {
        return;
      }

      const position = screenToFlowPosition({
        x: event.clientX - 30,
        y: event.clientY - 30,
      });

      const data: {
        projectId?: string,
        label: string,
        title?: string
      } = { label: `${type} node` }

      // 工作流传递的数据
      if (event.dataTransfer.getData("projectId")) {
        data.projectId = event.dataTransfer.getData("projectId")
        data.title = event.dataTransfer.getData("projectName")
      }

      const newNode = {
        id: `id_${Math.random().toString(36).slice(2, 9)}`,
        type,
        position,
        data,
      };

      setNodes((nds) => nds.concat(newNode));
    },
    [screenToFlowPosition, type]
  );

  const initData = async () => {
    const searchParams = new URLSearchParams(window.location.search);
    const projectId = searchParams.get('projectId');
    setProjectName(searchParams.get('projectName') || "")
    if (projectId == null) {
      message.error("请选择项目");
      return
    }
    const result = await getLlmView(Number(projectId));
    const nodesJson = JSON.parse(result.data[0]);
    const edgesJson = JSON.parse(result.data[1]);

    setNodes(nodesJson);
    setEdges(edgesJson);
  };

  useEffect(() => {
    initData();
    const searchParams = new URLSearchParams(window.location.search);
    const projectId = searchParams.get('projectId');

    if (!projectId) {
      navigate("/rag/project")
    }
  }, []);

  // 保存视图
  const saveData = async () => {
    const searchParams = new URLSearchParams(window.location.search);
    const projectId = searchParams.get('projectId');
    if (projectId == null) {
      message.error("请选择项目");
      return
    }

    const result = await saveLlmView({
      projectId: Number(projectId),
      nodes: JSON.stringify(nodes),
      edges: JSON.stringify(edges)
    })
    if (result.code == 200) {
      message.success("保存成功");
    } else {
      message.error("保存失败");
    }
  }

  // 快捷键操作
  const shortcutKey = (event) => {
    const activeElement = document.activeElement;
    const isInputFocused = activeElement && (activeElement.tagName === 'INPUT'
      || activeElement.tagName === 'TEXTAREA' || activeElement.tagName === 'DIV');

    if (!isInputFocused) {
      if (event.key === "Delete" || event.key === "Backspace") {
        setNodes((node) => node.filter((e) => e.id !== selectNodeId.current));
        setEdges((es) =>
          es.filter(
            (e) =>
              e.target !== selectNodeId.current && e.source !== selectNodeId.current
          )
        );
      }
      // 删除变量
      deteleVariable(selectNodeId.current)

      if (event.key === "s" && (event.metaKey || event.ctrlKey)) {
        event.preventDefault();
        saveData();
      }
    }
  }

  useEffect(() => {
    addEventListener("keydown", shortcutKey);
    return () => removeEventListener("keydown", shortcutKey)
  }, [nodes, edges])


  const [openLlmTable, setOpenLlmTable] = useState(false)


  return (
    <div className="overflow-hidden rounded-xl" style={{ height: '100%', width: "100%" }}>
      <div className="bg-[#FBFBFC] h-[67px] flex" style={{ borderBottom: '1px solid #E0E2EA' }}>

        <div className="flex pt-[13px]">
          <img src="img/return.png" className="h-[40px] ml-4 cursor-pointer"
            onClick={() => {
              navigate("/rag/project")
            }} />
          <span className="mt-2">{projectName}</span>
        </div>

        <div
          className="w-[calc(100%-360px)] flex justify-center items-center text-[16px]"
        >
          <Space size={40}>
            <span className="cursor-pointer" onClick={() => setApi(true)}>访问API</span>
            <span className="cursor-pointer" onClick={() => setLog(true)}>对话日志</span>
          </Space>
        </div>
      </div>
      <div className="dndflow h-[calc(100%-67px)]" style={{ width: "100%" }}>
        <ReactFlow
          nodes={nodes}
          edges={edges}
          onNodesChange={onNodesChange}
          onEdgesChange={onEdgesChange}
          onConnect={onConnect}
          edgeTypes={edgeTypes}
          nodeTypes={nodeTypes as any}
          maxZoom={1.6}
          connectionLineComponent={ConnectionLine}
          onDrop={onDrop}
          onDragOver={onDragOver}
          defaultEdgeOptions={{
            zIndex: 99999
          }}
          onNodeClick={(e, node) => (selectNodeId.current = node.id)}
          fitView
          fitViewOptions={{ padding: 0.1 }}
        >

          {/* <Panel position="top-right">
            <NodePanel />
          </Panel> */}
          <Controls
            style={{
              borderRadius: 8,
              overflow: 'hidden',
              color: '#485265',
              background: '#fff',
              padding: 6,
              marginLeft: 20,
              gap: 10,
            }}
            position="bottom-right"
            orientation="horizontal" >
          </Controls>

          <MiniMap className="rounded-xl overflow-hidden" style={{ marginBottom: 64, width: 150, height: 92, boxShadow: "rgba(19, 51, 107, 0.1) 0px 0px 1px, rgba(19, 51, 107, 0.1) 0px 4px 10px" }} />
          <Background variant={BackgroundVariant.Dots} gap={24} size={1} color="#96969F" style={{ background: '#F4F4F7' }} />
        </ReactFlow>

        <FloatButton
          className="hover:rotate-180 transition-transform duration-300"
          icon={<img src="img/fluent--add-12-filled.svg" />}
          onClick={() => { setOpenLlmTable(!openLlmTable) }}
          type="primary" style={{ top: 84, left: 22, height: 36, width: 36 }} />
        {openLlmTable && <LlmTabs onClose={() => setOpenLlmTable(!openLlmTable)} />}
      </div>

      {/* 显示API */}
      <Api open={api} onClose={() => setApi(false)} />

      {/* 日志 */}
      <Log open={log} onClose={() => setLog(false)} />

      {/* AI助手 */}
      <AiChat open={chatVisible} onClose={() => setChatVisible(false)} />

      {/* 知识图谱 */}
      <GraphRag open={graph} onClose={() => setGraph(false)} />

      {/* 功能按钮 */}
      <TooltipButton
        onClick={(type) => {
          if (type == "ai") {
            setChatVisible(true)
          }
          if (type == "save") {
            saveData()
          }
          if (type == "layout") {
            onLayout({ direction: 'RIGHT' })
          }
        }} />
    </div>
  );
};

export default () => {
  return (
    <ReactFlowProvider>
      <DnDProvider>
        <DnDFlow />
      </DnDProvider>
    </ReactFlowProvider>
  );
};
